<?php

namespace Smoren\ArrayView\Tests\Unit\ArraySliceView;

use Smoren\ArrayView\Selectors\SliceSelector;
use Smoren\ArrayView\Tests\Unit\Fixtures\PythonDataProviders;
use Smoren\ArrayView\Views\ArraySliceView;
use Smoren\ArrayView\Views\ArrayView;

class ReadTest extends \Codeception\Test\Unit
{
    use PythonDataProviders;

    /**
     * @dataProvider dataProviderForRead
     * @dataProvider dataProviderForSliceInBoundsPythonAutoGenerated
     * @dataProvider dataProviderForSliceOutOfBoundsPythonAutoGenerated
     */
    public function testReadByMethod(array $source, string $config, array $expected)
    {
        $view = ArrayView::toView($source);
        $subview = $view->subview($config);

        $this->assertInstanceOf(ArraySliceView::class, $subview);

        $this->assertSame($expected, [...$subview]);
        $this->assertSame(\count($expected), \count([...$subview]));

        for ($i = 0; $i < \count($subview); ++$i) {
            $this->assertSame($expected[$i], $subview[$i]);
        }

        for ($i = 0; $i < \count($view); ++$i) {
            $this->assertSame($source[$i], $view[$i]);
        }

        $this->assertSame($source, $view->toArray());
        $this->assertSame($expected, $subview->toArray());

        $this->assertSame($source, [...$view]);
        $this->assertSame($expected, [...$subview]);
    }

    /**
     * @dataProvider dataProviderForRead
     * @dataProvider dataProviderForSliceInBoundsPythonAutoGenerated
     * @dataProvider dataProviderForSliceOutOfBoundsPythonAutoGenerated
     */
    public function testReadByIndex(array $source, string $config, array $expected)
    {
        $view = ArrayView::toView($source);
        $slicedArray = $view[$config];

        $this->assertSame($expected, $slicedArray);
        $this->assertSame(\count($expected), \count($slicedArray));

        for ($i = 0; $i < \count($slicedArray); ++$i) {
            $this->assertSame($expected[$i], $slicedArray[$i]);
        }

        for ($i = 0; $i < \count($view); ++$i) {
            $this->assertSame($source[$i], $view[$i]);
        }

        $this->assertSame($source, $view->toArray());
        $this->assertSame($source, [...$view]);
        $this->assertSame($expected, $slicedArray);
    }

    /**
     * @dataProvider dataProviderForRead
     * @dataProvider dataProviderForSliceArraySubviewRead
     * @dataProvider dataProviderForSliceInBoundsPythonAutoGenerated
     * @dataProvider dataProviderForSliceOutOfBoundsPythonAutoGenerated
     */
    public function testReadByMethodAndIndex(array $source, $config, array $expected)
    {
        $view = ArrayView::toView($source);
        $selector = new SliceSelector($config);
        $this->assertSame($selector, $selector->getValue());

        $slicedArray = $view->subview($selector)[':'];
        $this->assertSame($expected, $slicedArray);
        $this->assertSame(\count($expected), \count($slicedArray));

        for ($i = 0; $i < \count($slicedArray); ++$i) {
            $this->assertSame($expected[$i], $slicedArray[$i]);
        }

        for ($i = 0; $i < \count($view); ++$i) {
            $this->assertSame($source[$i], $view[$i]);
        }

        $this->assertSame($source, $view->toArray());
        $this->assertSame($source, [...$view]);
        $this->assertSame($expected, $slicedArray);
    }

    public function dataProviderForRead(): array
    {
        return [
            [[1, 2, 3, 4, 5, 6, 7, 8, 9], '1:6', [2, 3, 4, 5, 6]],
            [[1, 2, 3, 4, 5, 6, 7, 8, 9], '1:6:1', [2, 3, 4, 5, 6]],
            [[1, 2, 3, 4, 5, 6, 7, 8, 9], '1:6:2', [2, 4, 6]],
            [[1, 2, 3, 4, 5, 6, 7, 8, 9], '2:8', [3, 4, 5, 6, 7, 8]],
            [[1, 2, 3, 4, 5, 6, 7, 8, 9], '2:8:1', [3, 4, 5, 6, 7, 8]],
            [[1, 2, 3, 4, 5, 6, 7, 8, 9], '2:8:2', [3, 5, 7]],

            [[1, 2, 3, 4, 5, 6, 7, 8, 9], '-1::-1', [9, 8, 7, 6, 5, 4, 3, 2, 1]],
            [[1, 2, 3, 4, 5, 6, 7, 8, 9], '-1:0:-1', [9, 8, 7, 6, 5, 4, 3, 2]],

            [[1, 2, 3, 4, 5, 6, 7, 8, 9], '0:9:1', [1, 2, 3, 4, 5, 6, 7, 8, 9]],
            [[1, 2, 3, 4, 5, 6, 7, 8, 9], '0:9:2', [1, 3, 5, 7, 9]],
            [[1, 2, 3, 4, 5, 6, 7, 8, 9], '1:9:2', [2, 4, 6, 8]],
            [[1, 2, 3, 4, 5, 6, 7, 8, 9], '0:10:1', [1, 2, 3, 4, 5, 6, 7, 8, 9]],
            [[1, 2, 3, 4, 5, 6, 7, 8, 9], '0:10:2', [1, 3, 5, 7, 9]],
            [[1, 2, 3, 4, 5, 6, 7, 8, 9], '-9:9:1', [1, 2, 3, 4, 5, 6, 7, 8, 9]],
            [[1, 2, 3, 4, 5, 6, 7, 8, 9], '-9:9:2', [1, 3, 5, 7, 9]],
            [[1, 2, 3, 4, 5, 6, 7, 8, 9], '-10:10:1', [1, 2, 3, 4, 5, 6, 7, 8, 9]],
            [[1, 2, 3, 4, 5, 6, 7, 8, 9], '-10:10:2', [1, 3, 5, 7, 9]],
            [[1, 2, 3, 4, 5, 6, 7, 8, 9], '-5:10:1', [5, 6, 7, 8, 9]],
            [[1, 2, 3, 4, 5, 6, 7, 8, 9], '-5:100:2', [5, 7, 9]],

            [[], '0:', []],
            [[], '0:0', []],
            [[], '0:0:1', []],
            [[], '1:-1', []],
            [[], '-1:-1', []],
            [[], '-2:-1', []],
            [[], '-2:-1:2', []],
            [[], '-1:0:-1', []],
            [[1], '0:', [1]],
            [[1], '0:1', [1]],
            [[1], '0:1:1', [1]],
            [[1], '0:1:2', [1]],
            [[1], '0:-1', []],
            [[1], '0:-1:1', []],
            [[1], '0:-1:2', []],
            [[1], '0:10:100', [1]],
            [[1], '1:10:100', []],
            [[1], '0:', [1]],
            [[1, 2, 3], '0:0:1', []],
            [[1], '1:', []],
            [[1, 2], '1:0', []],
            [[1, 2], '1::-1', [2, 1]],
            [[1, 2], '0:1', [1]],
            [[1, 2], '1:1', []],
            [[1, 2, 3, 4, 5, 6, 7, 8, 9], '1::2', [2, 4, 6, 8]],
            [[1, 2, 3, 4, 5, 6, 7, 8, 9], '::2', [1, 3, 5, 7, 9]],
        ];
    }

    public function dataProviderForSliceArraySubviewRead(): array
    {
        return [
            [[1, 2, 3, 4, 5, 6, 7, 8, 9], [1,6], [2, 3, 4, 5, 6]],
            [[1, 2, 3, 4, 5, 6, 7, 8, 9], [1,6,1], [2, 3, 4, 5, 6]],
            [[1, 2, 3, 4, 5, 6, 7, 8, 9], [1,6,2], [2, 4, 6]],
            [[1, 2, 3, 4, 5, 6, 7, 8, 9], [2,8], [3, 4, 5, 6, 7, 8]],
            [[1, 2, 3, 4, 5, 6, 7, 8, 9], [2,8,1], [3, 4, 5, 6, 7, 8]],
            [[1, 2, 3, 4, 5, 6, 7, 8, 9], [2,8,2], [3, 5, 7]],
        ];
    }
}
